-
Notifications
You must be signed in to change notification settings - Fork 226
adds code generation for field comparisons #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
vijtrip2
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
a-hilaly
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C'est genial @jaypipes! ça se rapproche!
I left few comments and questions
| opts := []cmp.Option{cmpopts.EquateEmpty()} | ||
| {{- if .CRD.CompareIgnoredFields }} | ||
| opts = append(opts, cmpopts.IgnoreFields(*ac.ko, | ||
| {{- range $fieldPath := .CRD.CompareIgnoredFields }} | ||
| {{ printf "%q" $fieldPath }}, | ||
| {{- end }} | ||
| )) | ||
| {{- end }} | ||
| return cmp.Equal(ac.ko, bc.ko, opts...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the modules github.com/google/go-cmp/cmp and github.com/google/go-cmp/cmp/cmpopts are no longer used we can remove them from the template (line 8,9)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 great catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
templates/pkg/resource/delta.go.tpl
Outdated
| package {{ .CRD.Names.Snake }} | ||
|
|
||
| import ( | ||
| ackcompare "github.com/aws/aws-controllers-k8s/pkg/compare" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/github.com/aws/aws-controllers-k8s/ github.com/aws-controllers-k8s/runtime/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yikes, great catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
| // Delta returns an `ackcompare.Delta` object containing the difference between | ||
| // one `AWSResource` and another. | ||
| func (d *resourceDescriptor) Delta(a, b acktypes.AWSResource) *ackcompare.Delta { | ||
| return newResourceDelta(a, b) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generated code doesn't compile since a type is an interface. I'm not very sure, but i think we can use newResourceDelta(a.(*resource), b.(*resource)) (thinking about type assertion panics..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
| latest *resource, | ||
| diffReporter *ackcompare.Reporter, | ||
| delta *ackcompare.Delta, | ||
| ) (*resource, error) { | ||
| {{ $customMethod := .CRD.GetCustomImplementation .CRD.Ops.Update }} | ||
| {{ if $customMethod }} | ||
| customResp, customRespErr := rm.{{ $customMethod }}(ctx, desired, latest, diffReporter) | ||
| customResp, customRespErr := rm.{{ $customMethod }}(ctx, desired, latest, delta) | ||
| if customResp != nil || customRespErr != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do the same for the other update methods in manager.go.tpl, sdk_update_set_attributes.go.tpl, sdk_update_not_implemented.go.tpl and sdk_update_custom.go.tpl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
| delta.Add("", a, b) | ||
| return delta | ||
| } | ||
| {{ GoCodeCompare .CRD "delta" "a.ko" "b.ko" 1}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a "GoCodeCompare" implementation in pkg/generate/ack/controller.go e.g:
"GoCodeCompare": func(r *ackmodel.CRD, deltaVarName, firstResVarName string, secondResVarName string, indentLevel int) string {
return code.CompareResource(r.Config(), r, deltaVarName, firstResVarName, secondResVarName, indentLevel)
},There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also in pkg/generate/ack/controller.go "delta.go.tpl" should be added the targets list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
pkg/generate/code/compare_test.go
Outdated
| if ackcompare.HasNilDifference(a.ko.Spec.ACL, b.ko.Spec.ACL) { | ||
| delta.Add("Spec.ACL", a.ko.Spec.ACL, b.ko.Spec.ACL) | ||
| } else { | ||
| if *a.ko.Spec.ACL != *b.ko.Spec.ACL { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While testing the s3 controller (generated with the new compare methods) i observed few runtime panics, and the reason is that we don't handle cases where both of a.ko.Spec.* and a.ko.Spec.* are nil pointers. Calling "valueof" of a nil pointer causes panic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick fix (Probably there is a better way..): a-hilaly@f787376 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another excellent catch, @a-hilaly! I went with a route that generated:
} else if a.ko.Spec.Name != nil && b.ko.Spec.Name != nil {instead of an additional indent-level if statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! 👍
pkg/generate/config/field.go
Outdated
| IsIgnored bool `json:"is_ignored"` | ||
| // NilEqualsEmptyString indicates a nil string pointer and empty string | ||
| // should be considered equal for the purposes of comparison | ||
| NilEqualsEmptyString bool `json:"nil_equals_empty_string"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we should rename this field to NilEqualsZeroValue? i'm thinking about cases where we want to equate 0 and nil, 0.0 and nil, false and nil ... etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome point.
Adds new code generation functions to `pkg/generate/code` that output Go
code that compares fields of various types and calls `delta.Add()` if a
difference is detected.
For fields with underlying scalar types, the generated code looks like
this:
```go
if *a.ko.Spec.Name != *b.ko.Spec.Name) {
delta.Add("Spec.Name", a.ko.Spec.Name, b.ko.Spec.Name)
}
```
For non-scalar types, generated code recurses over the fields and/or
calls specialized comparison functions in the runtime library's
`pkg/compare` module. Here is an example of a complex underlying field
type and what the generated code might look like:
```go
if ackcompare.HasNilDifference(a.ko.Spec.CreateBucketConfiguration, b.ko.Spec.CreateBucketConfiguration) {
delta.Add("Spec.CreateBucketConfiguration", a.ko.Spec.CreateBucketConfiguration, b.ko.Spec.CreateBucketConfiguration)
} else {
if ackcompare.HasNilDifference(a.ko.Spec.CreateBucketConfiguration.LocationConstraint, b.ko.Spec.CreateBucketConfiguration.LocationConstraint) {
delta.Add("Spec.CreateBucketConfiguration.LocationConstraint", a.ko.Spec.CreateBucketConfiguration.LocationConstraint, b.ko.Spec.CreateBucketConfiguration.LocationConstraint)
} else {
if *a.ko.Spec.CreateBucketConfiguration.LocationConstraint != *b.ko.Spec.CreateBucketConfiguration.LocationConstraint {
delta.Add("Spec.CreateBucketConfiguration.LocationConstraint", a.ko.Spec.CreateBucketConfiguration.LocationConstraint, b.ko.Spec.CreateBucketConfiguration.LocationConstraint)
}
}
}
```
Adds plumbing to the `pkg/resource/depscriptor.go.tpl` template that implements the `Delta()` method on the `github.com/aws-controllers-k8s/runtime/pkg/types.AWSResourceDescriptor` interface-implementing `resource` struct. Also adds a test for the compare field ignore functionality.
adds code generation for field comparisons
Adds new code generation functions to
pkg/generate/codethat output Gocode that compares fields of various types and calls
delta.Add()if adifference is detected.
For fields with underlying scalar types, the generated code looks like
this:
For non-scalar types, generated code recurses over the fields and/or
calls specialized comparison functions in the runtime library's
pkg/comparemodule. Here is an example of a complex underlying fieldtype and what the generated code might look like:
Adds plumbing to the
pkg/resource/depscriptor.go.tpltemplate thatimplements the
Delta()method on thegithub.com/aws-controllers-k8s/runtime/pkg/types.AWSResourceDescriptorinterface-implementing
resourcestruct.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.